<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */

namespace app\adminapi\controller\v1\wf;

use app\adminapi\controller\AuthController;
use app\Request;
use lflow\ckpt\EndCkpt;
use lflow\ckpt\StartCkpt;
use lflow\core\WorkFlowEngine;
use lflow\exceptions\WorkFlowException;
use lflow\lib\enum\WfOrderStateEnum;
use lflow\lib\interface\WfConstants;
use lflow\lib\util\ObjectHelper;
use lflow\lib\util\StringHelper;
use think\App;
use think\facade\Db;

class Task extends AuthController
{
    protected WorkFlowEngine $service;

    public function __construct(App $app, WorkFlowEngine $service)
    {
        parent::__construct($app);
        $this->service = $service;
    }

    /**
     * 我的已办
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function doneList(Request $request): \think\Response\Json
    {
        $engine = $this->service;
        $where  = $this->request->getMore([
            ['operator', $request->adminId()],
            ['display_name', ''],
        ]);
        $result = $engine->query()->getHistoryWorkItems($where);
        foreach ($result['list'] as $key => $item) {
            $orders = $item->getData('orders');
            if (!empty($orders)) {
                if (is_string($orders->getData('variable'))) {
                    $orders->set('variable', json_decode($orders->getData('variable')));
                }
            }
            $process = $item->getData('processs');
            if (!empty($process)) {
                if (is_string($process->getData('content'))) {
                    $process->set('content', json_decode($process->getData('content')));
                }
            }
        }
        return app('json')->success('获取成功', $result);
    }

    /**
     * 我的待办
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function todolist(Request $request): \think\Response\Json
    {
        $engine = $this->service;
        $where  = $this->request->getMore([
            ['actor_id', $request->adminId()],
            ['display_name', ''],
        ]);
        $result = $engine->query()->getWorkItems($where, true);
        foreach ($result['list'] as $key => $item) {
            $orders = $item->getData('orders');
            if (!empty($orders)) {
                if (is_string($orders->getData('variable'))) {
                    $orders->set('variable', json_decode($orders->getData('variable')));
                }
            }
            $process = $item->getData('processs');
            if (!empty($process)) {
                if (is_string($process->getData('content'))) {
                    $process->set('content', json_decode($process->getData('content')));
                }
            }
        }
        return app('json')->success('获取成功', $result);
    }

    /**
     * 执行任务
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function execute(Request $request): \think\Response\Json
    {
        Db::startTrans();
        try {
            $engine   = $this->service;
            $param    = $request->postMore([['task_id'], ['args']]);
            $args     = $param['args'] ?? [];
            $userInfo = $request->adminInfo();

            //构造参数
            $args = ObjectHelper::putAll((object)$args, (object)[
                WfConstants::ORDER_USER_REAL_NAME_KEY => $userInfo['real_name'] ?? '',
                WfConstants::ORDER_USER_NAME_KEY      => $userInfo['account'] ?? '',
                WfConstants::ORDER_USER_ID            => $userInfo['id'] ?? '',
            ]);

            $engine->executeTask($param['task_id'], $request->adminId(), $args);
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException $e) {
            Db::rollback();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 任务驳回
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function backOff(Request $request): \think\Response\Json
    {
        try {

            Db::startTrans();
            $engine = $this->service;
            $param  = $request->postMore([['task_id'], ['args']]);
            $task   = $engine->query()->getTask($param['task_id']);
            if (empty($task)) {
                throw new WorkFlowException('任务不存在或 被删除');
            }

            $order = $engine->query()->getOrder($task->getData('order_id'));
            if (empty($order)) {
                throw new WorkFlowException('实例不存在或 被删除');
            }

            $sourceNodeName = $param['args'][WfConstants::TARGET_NODE_NAME] ?? '';

            $historyTasks = $engine->query()->getHistoryTasks(['order_id' => $order->getData('id')]);

            // 节点不存在，则使用上一个任务节点
            if (empty($sourceNodeName)) {
                if (empty($historyTasks['list'])) {
                    // 不存在历史任务，则直接驳回
                    $this->rejectOrder($order->getData('process_id'), $order->getData('id'), $task->getData('id'));
                    Db::commit();
                    return app('json')->success('操作成功');
                } else if (StringHelper::equalsIgnoreCase(WfConstants::FIRST_TASK_NAME, $historyTasks['list'][0]->getData('task_name'))) {
                    // 如果第一个节点为申请任务节点，也直接驳回流程
                    $this->rejectOrder($order->getData('process_id'), $order->getData('id'), $task->getData('id'));
                    Db::commit();
                    return app('json')->success('操作成功');
                }
            }

            //找对应节点task历史
            $historyTask = null;;
            foreach ($historyTasks['list'] as $item) {
                if ($item->getData('task_name') == $sourceNodeName) {
                    $historyTask = $item;
                }
            }

            // 找不到对应节点-直接驳回
            if (empty($historyTask)) {
                $this->rejectOrder($order->getData('process_id'), $order->getData('id'), $task->getData('id'));
                Db::commit();
                return app('json')->success('操作成功');
            }
            // 如果第一个节点为申请任务节点，也直接驳回流程
            $processCkpt = $engine->process()->getProcessById($order->getData('process_id'))->getData('process_ckpt');
            if ($processCkpt) {
                foreach ($processCkpt->getNodes() as $tm) {
                    if ($tm instanceof StartCkpt) {
                        if (StringHelper::equalsIgnoreCase($tm->getName(), $sourceNodeName)) {
                            $this->rejectOrder($order->getData('process_id'), $order->getData('id'), $task->getData('id'));
                            Db::commit();
                            return app('json')->success('操作成功');
                        }
                    }
                }
            }

            $this->rollbackNode($task->getData('id'), $sourceNodeName, $param['args']);
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException $e) {
            Db::rollback();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 驳回任务指定节点
     *
     * @param string       $taskId
     * @param string       $nodeName
     * @param object|array $args
     */
    private function rollbackNode(string $taskId, string $nodeName, object|array $args): void
    {
        // 1.回退流程
        // 1.1 跳到指定节点
        $args   = is_array($args) ? (object)$args : $args;
        $engine = new WorkFlowEngine();
        $engine->executeAndJumpTask($taskId, $engine::ADMIN, $args, $nodeName);
    }

    /**
     * 驳回流程
     *
     * @param string $processId
     * @param string $orderId
     * @param string $taskId
     */
    private function rejectOrder(string $processId, string $orderId, string $taskId): void
    {
        $userInfo = app()->request->adminInfo();
        // 1.驳回流程
        // 1.1 给流程实例追加额外参数
        $args   = [
            WfConstants::ORDER_STATE_KEY => WfOrderStateEnum::REJECT()->ordinal(),
            WfConstants::REMARK          => "【" . $userInfo['real_name'] . "】驳回流程",

        ];
        $engine = new WorkFlowEngine();
        $engine->order()->addVariable($orderId, (object)$args);
        // 1.2 直接跳到结束节点
        $processCkpt = $engine->process()->getProcessById($processId)->getData('process_ckpt');
        $engine->executeAndJumpTask($taskId, $engine::ADMIN, (object)$args, $processCkpt->getCkpts(new EndCkpt())[0]->getName());
    }

    /**
     * 获取任务详情
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function get(Request $request): \think\Response\Json
    {
        $engine = $this->service;
        $where  = $request->postMore([['id']]);
        $data   = $engine->query()->getTaskDetails($where);
        if (empty($data)) {
            $data = $engine->query()->getHistoryTaskDetails($where);
        }

        if (!empty($data)) {
            $orders  = $data->getData('orders');
            $process = $data->getData('processs');
            if (!empty($orders)) {
                $orders->set('variable', json_decode($orders->getData('variable')));
            }
            if (!empty($process)) {
                $process->set('content', json_decode($process->getData('content')));
            }
        }
        $data = is_null($data) ? [] : $data->toArray();
        return app('json')->success($data);
    }

    /**
     * 获取审批历史记录
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function listHisByOrderId(Request $request): \think\Response\Json
    {
        $engine = $this->service;
        $where  = $request->postMore([['id', '', '', 'order_id']]);
        $result = $engine->query()->getHistoryTasks($where, 'finish_time asc');
        return app('json')->success($result['list']->toArray());
    }

}
